package com.swipeclose;


import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.graphics.Rect;
import android.support.v4.view.ViewPager;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.Scroller;

public class SwipeToClose {

	private static final String TAG 			= SwipeToClose.class.getName();
	private static final int 	INVALID_POINTER = -1;
	
	private boolean mIsBeingDragged;
	private boolean mInchild;//是否touch在某些横向滑动的view
	
	private int 	mTouchSlop;//最小移动距离
	private int		mActivePointerId;
	private int 	mLastMotionX;
	private int 	mLastMotionY;
	private int 	mMoveMotionX;
	private int 	mMaximumVelocity;
	private int 	mMinimumVelocity;
	
	private View				mView;
	private VelocityTracker 	mVelocityTracker;
	private Scroller			mScroller;
	private SwipeToCloseAction 	mAction;
	private List<View> 			mHorizeScrollviews;
	
	public SwipeToClose(Context context, Scroller scroller) {
		mScroller = scroller;
		final ViewConfiguration configuration = ViewConfiguration.get(context);
		mTouchSlop = configuration.getScaledTouchSlop();
		mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
		mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
		mHorizeScrollviews = new ArrayList<View>();
	}
	
	public void setContentView(View view){
		this.mView = view;
	}
	
	public void setSwipeToCloseAction(SwipeToCloseAction action) {
		this.mAction = action;
	}
	
	/**
	 * 添加横向干扰的view
	 * @param view
	 */
	public void addHorizeScrollviews(View view) {
		if(mHorizeScrollviews.contains(view)){
			this.mHorizeScrollviews.add(view);
		}
	}
	
	public boolean onInterceptTouchEvent(MotionEvent ev) {
        final int action = ev.getAction();
        if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {
            return true;
        }
        switch (action & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_MOVE: {
                final int activePointerId = mActivePointerId;
                if (activePointerId == INVALID_POINTER) {
                    break;
                }

                final int pointerIndex = ev.findPointerIndex(activePointerId);
                if (pointerIndex == -1) {
                    break;
                }

                final int x = (int) ev.getX(pointerIndex);
                final int y = (int) ev.getY(pointerIndex);
                final int xDiff = Math.abs(x - mLastMotionX);
                final int yDiff = Math.abs(y - mLastMotionY);
//                Log.i("TAG", "mInchild:" + mInchild + " ----------- mCanMove:" + mCanMove);
//                Log.i("TAG", "xDiff:" + xDiff + " ----------- mTouchSlop:" + mTouchSlop);
                boolean hasCanScroll = hasCanScroll(x, y);
                if (xDiff > yDiff && xDiff > mTouchSlop && hasCanScroll) {
                    mIsBeingDragged = true;
                    mLastMotionX = x;
                    mMoveMotionX = x;
                    initVelocityTrackerIfNotExists();
                    mVelocityTracker.addMovement(ev);
                    final ViewParent parent = mView.getParent();
                    if (parent != null) {
                        parent.requestDisallowInterceptTouchEvent(true);
                    }
                }
                break;
            }

            case MotionEvent.ACTION_DOWN: {
                final int x = (int) ev.getX();
                mLastMotionX = x;
                mMoveMotionX = x;
                mLastMotionY = (int) ev.getY();
                mActivePointerId = ev.getPointerId(0);

                initOrResetVelocityTracker();
                mVelocityTracker.addMovement(ev);

                mIsBeingDragged = !mScroller.isFinished();
                mInchild = inchild(mMoveMotionX, mLastMotionY);
                
                if(mAction != null && !mIsBeingDragged){
                	mAction.onTouchDown();
                }
                break;
            }

            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                mIsBeingDragged = false;
                mActivePointerId = INVALID_POINTER;
                recycleVelocityTracker();
                break;
            case MotionEvent.ACTION_POINTER_UP:
                onSecondaryPointerUp(ev);
                break;
        }
        return mIsBeingDragged;
	}
	
	private void onSecondaryPointerUp(MotionEvent ev) {
        final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
        final int pointerId = ev.getPointerId(pointerIndex);
        if (pointerId == mActivePointerId) {
            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
            mLastMotionX = (int) ev.getX(newPointerIndex);
            mActivePointerId = ev.getPointerId(newPointerIndex);
            if (mVelocityTracker != null) {
                mVelocityTracker.clear();
            }
        }
    }
	
	public boolean onTouchEvent(MotionEvent ev) {
        initVelocityTrackerIfNotExists();
        MotionEvent vtev = MotionEvent.obtain(ev);
        final int actionMasked = ev.getActionMasked();
        switch (actionMasked) {
            case MotionEvent.ACTION_DOWN: {
                if (mView instanceof ViewGroup) {
                	if(((ViewGroup) mView).getChildCount() == 0)
                    return false;
                }
                if ((mIsBeingDragged = !mScroller.isFinished())) {
                    final ViewParent parent = mView.getParent();
                    if (parent != null) {
                        parent.requestDisallowInterceptTouchEvent(true);
                    }
                }

                if (!mScroller.isFinished()) {
                    mScroller.abortAnimation();
                }

                mLastMotionX = (int) ev.getX();
                mMoveMotionX = mLastMotionX;
                mActivePointerId = ev.getPointerId(0);
                break;
            }
            case MotionEvent.ACTION_MOVE:
                final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
                if (activePointerIndex == -1) {
                    Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
                    break;
                }

                final int x = (int) ev.getX(activePointerIndex);
                int deltaX = mMoveMotionX - x;
                if (!mIsBeingDragged && Math.abs(deltaX) > mTouchSlop) {
                    final ViewParent parent = mView.getParent();
                    if (parent != null) {
                        parent.requestDisallowInterceptTouchEvent(true);
                    }
                    mIsBeingDragged = true;
                    if (deltaX > 0) {
                        deltaX -= mTouchSlop;
                    } else {
                        deltaX += mTouchSlop;
                    }
                }
                if (mIsBeingDragged) {
                	if(mView.getScrollX() + deltaX >= 0){
                		deltaX = -mView.getScrollX();
                	}
                	mView.scrollBy(deltaX, 0);
                }
                mMoveMotionX = x;
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
            	if (mIsBeingDragged) {
                    final VelocityTracker velocityTracker = mVelocityTracker;
                    velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
                    int initialVelocity = (int) velocityTracker.getXVelocity(mActivePointerId);
                    int scrollX = mView.getScrollX();
                    int dx = Math.abs(scrollX);
                    boolean finish = false;
                    if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
                    	if(initialVelocity > 0){
                    		finish = true;
                    		dx = -(mView.getWidth() - dx);
                    	}
                    }else if(Math.abs(mLastMotionX - ev.getX()) > mView.getWidth() / 2){
                    	finish = true;
                    	dx = -(mView.getWidth() - dx);
                    }
                    scrollTo(scrollX, dx, finish);
                    mActivePointerId = INVALID_POINTER;
                }
                break;
            case MotionEvent.ACTION_POINTER_DOWN: {
                final int index = ev.getActionIndex();
                mLastMotionX = (int) ev.getX(index);
                mMoveMotionX = mLastMotionX;
                mActivePointerId = ev.getPointerId(index);
                break;
            }
            case MotionEvent.ACTION_POINTER_UP:
                onSecondaryPointerUp(ev);
                mLastMotionX = (int) ev.getX(ev.findPointerIndex(mActivePointerId));
                mMoveMotionX = mLastMotionX;
                break;
        }

        if (mVelocityTracker != null) {
            mVelocityTracker.addMovement(vtev);
        }
        vtev.recycle();
        
        if(mAction != null){
        	mAction.onScroll();
        }
        return true;
    }
	
	private void scrollTo(int startX, int dx, boolean finish){
		if(mAction != null){
			mAction.startScroll(startX, dx, finish);
        }
	}
	
	public interface SwipeToCloseAction{
		
		void startScroll(int startX, int dx, boolean finish);
		
		void onTouchDown();
		
		void onScroll();
	}
	
	/**
	 * 是否点击在可滑动的横向view里面
	 * @param x
	 * @param y
	 * @return
	 */
	private boolean inchild(int x, int y){
		int count = mHorizeScrollviews.size();
		for (int i = 0; i < count; i++) {
			View view = mHorizeScrollviews.get(i);
			if(view != null){
				Rect rect = new Rect();
				view.getGlobalVisibleRect(rect);
				if(rect.contains(x, y)){
					return true;
				}
			}
		}
		return false;
	}
	
	/**
	 * 必须实现,不然横向滑动的view会被屏蔽
	 * @param x
	 * @param y
	 * @return
	 */
	private boolean hasCanScroll(int x, int y){
		if(mInchild){
			int size = mHorizeScrollviews.size();
			for (int i = 0; i < size; i++) {
				View view = mHorizeScrollviews.get(i);
				if(view == null){
					continue;
				}else{
					if(mOnlyOneScrollCompareViewListener != null){
						if(mOnlyOneScrollCompareViewListener.onScrollCompare(view, mLastMotionX, x)){
							return true;
						}
					}
				}
				
			}
		}
		return true;
	}
	
	/**
	 * 是否是ViewPager
	 */
	public static boolean compareViewPager(View view, int startDownX, int lastDownX){
		boolean canScroll = true;
		ViewPager viewPager = (ViewPager) view;
		if(viewPager.getCurrentItem() == 0){
			if(lastDownX - startDownX < 0){
				canScroll = false;
        	}
		}else{
			canScroll = false;
		}
		return canScroll;
	}
	
	private void initOrResetVelocityTracker() {
        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
        } else {
            mVelocityTracker.clear();
        }
    }
	
	private void initVelocityTrackerIfNotExists() {
        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
        }
    }

    private void recycleVelocityTracker() {
        if (mVelocityTracker != null) {
            mVelocityTracker.recycle();
            mVelocityTracker = null;
        }
    }
    
    public static OnScrollCompareViewListener mOnlyOneScrollCompareViewListener;
    /**
     * 只添加一次,最好在Application里面实现
     * @param l
     */
    public static void setOnlyOneScrollCompareViewListenerOnApplication(OnScrollCompareViewListener l) {
		if(mOnlyOneScrollCompareViewListener == null){
			SwipeToClose.mOnlyOneScrollCompareViewListener = l;
		}
	}
    
    public interface OnScrollCompareViewListener{
    	boolean onScrollCompare(View view, int startDownX, int lastDownX);
    }
    
}
